package com.xuzimian.globaldemo.spring.core.jpa.proxy;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AssignableTypeFilter;

import java.util.Optional;

public class JpaRepostoryRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                AnnotationMetadata metadata = beanDefinition.getMetadata();
                return metadata.isIndependent() && metadata.isInterface() && !JpaRepository.class.getName().equals(beanDefinition.getBeanClassName());
            }
        };
        provider.addIncludeFilter(new AssignableTypeFilter(JpaRepository.class));

        for (BeanDefinition beanDefinition : provider.findCandidateComponents("com.xuzimian.globaldemo.spring.core.proxy")) {
            GenericBeanDefinition proxyBeanDefinition = new GenericBeanDefinition();
            proxyBeanDefinition.setFactoryBeanName("jpaRepositoryFactory");
            proxyBeanDefinition.setFactoryMethodName("createRepository");
            registry.registerBeanDefinition(getBeanClassName(beanDefinition), proxyBeanDefinition);
        }
    }

    private String getBeanClassName(BeanDefinition beanDefinition) {
        return Optional.ofNullable(beanDefinition.getBeanClassName())
                .map(beanClassName -> beanClassName.split("\\."))
                .filter(packages -> packages.length > 0)
                .map(packages -> packages[packages.length - 1])
                .map(className -> className.substring(0, 1).toLowerCase() + className.substring(1))
                .orElseGet(() -> StringUtils.EMPTY);
    }
}
